/*
 * Copyright (C) 2013 Manoel Afonso Filho
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package agimagem.util;

/**
 * Chave de uma determinada geração. Seus parâmetros permitem que se retorne a
 * sequência de bits do Indivíduo na geração <code>x</code> para a sequência da
 * geração <code>x - 1</code>. Assim, a decriptografia é realizada utilizando-se
 * todas as chaves de um indivíduo em ordem inversa a qual foram criadas sobre
 * o ciphertext.
 * @author Manoel Afonso Filho
 */
public class AGChave {

    private int a = Integer.MIN_VALUE;
    private int b = Integer.MIN_VALUE;
    private int k = Integer.MIN_VALUE;
    /**
     * <p>
     * Limite dos bytes relevantes. Durante a derivação, os bytes do arquivo
     * original são divididos em N vetores de 8 posições cada. Porém, caso o
     * número de bytes do arquivo não seja divisível por 8, será criado um vetor
     * a mais para comportar os bytes que sobraram, e o resto do vetor ficará
     * preenchido com bytes de valor 0. Assim, este atributo marca a última
     * posição que contem os bytes da imagem original, antes dos zeros restantes.
     * <b>Este limite é simplesmente a quantidade de bytes do arquivo original.</b>
     * </p>
     * <p>
     * Seja X o número de bytes do arquivo original, e se <code>limite > 0</code>,
     * então o arquivo criptografado terá <code>X + ( X mod 8)</code> bytes.
     * </p>
     */
    public static int limiteDerivacao = Integer.MIN_VALUE;

    private byte z = Byte.MIN_VALUE;
    private int pontoCrossover = Integer.MIN_VALUE;
    private byte[] parteOriginal = null;
    public static byte[] iv = null;

    private byte pontoMutacao = Byte.MIN_VALUE;
    private byte byteOriginal;

    /**
     * Método de conveniência para inserir os dados da derivação.
     * @param a
     * @param b
     * @param k
     */
    public void setDerivacao(int a, int b, int k){
        this.a = a;
        this.b = b;
        this.k = k;
    }

    /**
     * Método de conveniência para inserir os dados do crossover.
     * @param z
     * @param pontoCrossover
     * @param parteOriginal
     */
    public void setCrossover(byte z, int pontoCrossover, byte[] parteOriginal){
        this.z = z;
        this.pontoCrossover = pontoCrossover;

        byte[] novo = new byte[parteOriginal.length];
        System.arraycopy(parteOriginal, 0, novo, 0, parteOriginal.length);
        this.parteOriginal = novo;
    }

    /**
     * Método de conveniência para inserir os dados da mutação.
     * @param pontoMutacao
     * @param byteOriginal
     */
    public void setMutacao(byte pontoMutacao, byte byteOriginal){
        this.pontoMutacao = pontoMutacao;
        this.byteOriginal = byteOriginal;
    }

    /**
     * @return the a
     */
    public int getA() {
        return a;
    }

    /**
     * @param a the a to set
     */
    public void setA(int a) {
        this.a = a;
    }

    /**
     * @return the b
     */
    public int getB() {
        return b;
    }

    /**
     * @param b the b to set
     */
    public void setB(int b) {
        this.b = b;
    }

    /**
     * @return the k
     */
    public int getK() {
        return k;
    }

    /**
     * @param k the k to set
     */
    public void setK(int k) {
        this.k = k;
    }

    /**
     * @return the z
     */
    public byte getZ() {
        return z;
    }

    /**
     * @param z the z to set
     */
    public void setZ(byte z) {
        this.z = z;
    }

    /**
     * @return the pontoCrossover
     */
    public int getPontoCrossover() {
        return pontoCrossover;
    }

    /**
     * @param pontoCrossover the pontoCrossover to set
     */
    public void setPontoCrossover(int pontoCrossover) {
        this.pontoCrossover = pontoCrossover;
    }

    /**
     * @return Uma cópia do array contendo a Parte Original, ou null caso não
     * exista.
     */
    public byte[] getParteOriginal() {
        if(parteOriginal != null){
            byte[] novo = new byte[parteOriginal.length];
            System.arraycopy(parteOriginal, 0, novo, 0, parteOriginal.length);
            return novo;
        } else{
            return null;
        }

    }

    /**
     * @param parteOriginal the parteOriginal to set
     */
    public void setParteOriginal(byte[] parteOriginal) {  //FIXME Requer bastante tempo para executar
//        byte[] novo = new byte[parteOriginal.length];
//        System.arraycopy(parteOriginal, 0, novo, 0, parteOriginal.length);
//        this.parteOriginal = novo;
        this.parteOriginal = new byte[parteOriginal.length];
        System.arraycopy(parteOriginal, 0, this.parteOriginal, 0, parteOriginal.length);
    }

    /**
     * @return the pontoMutacao
     */
    public byte getPontoMutacao() {
        return pontoMutacao;
    }

    /**
     * @param pontoMutacao the pontoMutacao to set
     */
    public void setPontoMutacao(byte pontoMutacao) {
        this.pontoMutacao = pontoMutacao;
    }

    /**
     * @return the byteOriginal
     */
    public byte getByteOriginal() {
        return byteOriginal;
    }

    /**
     * @param byteOriginal the byteOriginal to set
     */
    public void setByteOriginal(byte byteOriginal) {
        this.byteOriginal = byteOriginal;
    }

    /**
     * Realiza o deep cloning deste objeto.
     * @return Um clone deste objeto.
     * @throws CloneNotSupportedException
     */
    @Override
    public Object clone() throws CloneNotSupportedException {
        AGChave novaChave = new AGChave();
        novaChave.setA(this.a);
        novaChave.setB(this.b);
        novaChave.setK(this.k);

        novaChave.setZ(this.z);
        novaChave.setPontoCrossover(this.pontoCrossover);
        if(this.parteOriginal != null){
            novaChave.setParteOriginal(this.parteOriginal);
        }

        novaChave.setPontoMutacao(this.pontoMutacao);
        novaChave.setByteOriginal(this.byteOriginal);
        return novaChave;
    }

    public void setCrossover2(byte pc) {
        this.pontoCrossover = pc;

//        this.setIv(new byte[iv.length]);
//        AGChave.iv = new byte[iv.length];
//        System.arraycopy(iv, 0, AGChave.iv, 0, iv.length);
    }

    /**
     * @return the iv
     */
    public byte[] getIv() {
        return iv;
    }

    /**
     * @param iv the iv to set
     */
    public void setIv(byte[] iv) {
        this.iv = iv;
    }

}
